<script setup lang="ts">
import type { AIModel } from '@/types/Ai'
import { ref, defineProps, onUnmounted, watch, nextTick } from 'vue'
import { useMemberStore } from '@/stores/modules/member'

const memberStore = useMemberStore()

const props = defineProps<{
  model: AIModel
  Message: string // 用户消息
}>()

// 响应式状态
const aiResponse = ref('')
const isStreaming = ref(false)
const error = ref('')
let controller: AbortController | null = null

// 监听用户消息变化
watch(
  () => props.Message,
  (newMsg) => {
    if (newMsg) {
      aiResponse.value = ''
      fetchAIStream(newMsg)
    }
  },
)

// 流式请求处理
const fetchAIStream = async (prompt: string) => {
  try {
    isStreaming.value = true
    error.value = ''
    controller = new AbortController()

    const response = await fetch('/api/ai/chat', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${memberStore.token}`,
      },
      body: JSON.stringify({
        prompt,
        model: props.model.id,
      }),
      signal: controller.signal,
    })

    if (!response.ok) {
      throw new Error(`请求失败: ${response.status}`)
    }

    const reader = response.body?.getReader()
    const decoder = new TextDecoder()
    let buffer = ''
    let textBuffer = ''

    const processChunk = () => {
      if (textBuffer.length > 0) {
        aiResponse.value += textBuffer
        textBuffer = ''
        scrollToBottom()
      }
      if (isStreaming.value) {
        requestAnimationFrame(processChunk)
      }
    }

    requestAnimationFrame(processChunk)

    while (reader && isStreaming.value) {
      const { done, value } = await reader.read()
      if (done) break

      buffer += decoder.decode(value, { stream: true })
      const chunks = buffer.split('\n')
      buffer = chunks.pop() || ''

      for (const chunk of chunks) {
        if (!chunk.trim()) continue
        try {
          const data = JSON.parse(chunk.replace(/^data: /, ''))
          if (data.choices?.[0]?.delta?.content) {
            textBuffer += data.choices[0].delta.content
          }
        } catch (err) {
          console.warn('解析异常:', chunk)
        }
      }
    }
  } catch (err) {
    if ((err as Error).name !== 'AbortError') {
      error.value = err instanceof Error ? err.message : '请求异常'
      console.error('请求失败:', err)
    }
  } finally {
    isStreaming.value = false
    controller = null
  }
}

// 滚动到底部
const scrollToBottom = () => {
  nextTick(() => {
    const container = document.querySelector('.chat-container')
    if (container) {
      container.scrollTop = container.scrollHeight
    }
  })
}

// 取消请求
const cancelStream = () => {
  if (controller) {
    controller.abort()
    isStreaming.value = false
  }
}

// 重试请求
const retry = () => {
  if (props.Message) {
    error.value = ''
    fetchAIStream(props.Message)
  }
}

// 组件卸载时清理
onUnmounted(cancelStream)
</script>

<template>
  <view class="chat-container">
    <!-- AI消息 -->
    <view class="message ai-message">
      <image class="avatar" :src="model.avatar" :alt="`${model.name}头像`" mode="aspectFill" />
      <view class="message-content">
        <text>{{ aiResponse }}</text>
        <!-- 加载状态 -->
        <view v-if="isStreaming" class="stream-indicator" role="status">
          <view class="dot"></view>
          <view class="dot"></view>
          <view class="dot"></view>
        </view>
      </view>
    </view>

    <!-- 用户消息 -->
    <view v-if="Message" class="message user-message">
      <view class="message-content">
        <text>{{ Message }}</text>
      </view>
      <image
        class="avatar"
        :src="
          memberStore.profile?.avatar ||
          'https://typo-img.oss-cn-chengdu.aliyuncs.com/img-localhost/202503200147244.jpg'
        "
        alt="用户头像"
        mode="aspectFill"
      />
    </view>

    <!-- 错误提示 -->
    <view v-if="error" class="error-tips">
      <text>{{ error }}</text>
      <button class="retry-btn" @tap="retry">重试</button>
    </view>
  </view>
</template>

<style lang="scss" scoped>
.chat-container {
  padding: 20rpx;
  min-height: 100vh;
  background: var(--page-bg, #fff);
}

.message {
  display: flex;
  gap: 20rpx;
  margin-bottom: 40rpx;
  animation: messageAppear 0.3s ease;

  &.ai-message {
    flex-direction: row;
  }

  &.user-message {
    flex-direction: row-reverse;
  }
}

.avatar {
  width: 80rpx;
  height: 80rpx;
  border-radius: 50%;
  flex-shrink: 0;
  border: 2rpx solid var(--border-color, #eee);
}

.message-content {
  max-width: 70%;
  padding: 20rpx 30rpx;
  border-radius: 20rpx;
  font-size: 28rpx;
  line-height: 1.5;
  position: relative;
  word-break: break-word;
  animation: contentAppear 0.2s ease;

  &::before {
    content: '';
    position: absolute;
    top: 20rpx;
    width: 0;
    height: 0;
    border: 10rpx solid transparent;
  }
}

.ai-message .message-content {
  background: var(--ai-msg-bg, #f5f5f5);
  color: var(--ai-msg-color, #333);

  &::before {
    left: -20rpx;
    border-right-color: var(--ai-msg-bg, #f5f5f5);
  }
}

.user-message .message-content {
  background: var(--user-msg-bg, #007aff);
  color: var(--user-msg-color, #fff);

  &::before {
    right: -20rpx;
    border-left-color: var(--user-msg-bg, #007aff);
  }
}

.stream-indicator {
  display: flex;
  gap: 8rpx;
  margin-top: 10rpx;

  .dot {
    width: 12rpx;
    height: 12rpx;
    background: var(--text-secondary, #666);
    border-radius: 50%;
    animation: dotPulse 1.4s infinite;

    &:nth-child(2) {
      animation-delay: 0.2s;
    }
    &:nth-child(3) {
      animation-delay: 0.4s;
    }
  }
}

.error-tips {
  color: var(--error, #ff4d4f);
  font-size: 24rpx;
  padding: 20rpx;
  text-align: center;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 20rpx;

  .retry-btn {
    padding: 8rpx 20rpx;
    border-radius: 20rpx;
    background: var(--error-bg, #ffeceb);
    color: var(--error, #ff4d4f);
    font-size: 24rpx;
  }
}

@keyframes messageAppear {
  from {
    opacity: 0;
    transform: translateY(20rpx);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes dotPulse {
  0%,
  100% {
    opacity: 0.3;
  }
  50% {
    opacity: 1;
  }
}
</style>
